﻿using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace InstantSpine.Core
{
    /// <summary>
    /// Describes a FE model of the spine via a set of anatomy
    /// parameters and material properties
    /// </summary>
    /// <remarks>
    /// This type is really rather a view model than a (pure) data model; properties 
    /// notify about changes (mutations) when they are updated (e.g. loaded from disc). 
    /// This class also handles commands issued by the view.
    /// </remarks>    
    [DataContract]
    public sealed class SpineModel : ISpineModel
    {   
        static readonly XmlWriterSettings xmlWriterSettings = new XmlWriterSettings { Indent = true };
        static readonly DataContractSerializer xmlSerializer = new DataContractSerializer(typeof(SpineModel));
        
        /// <summary>
        /// Material properties for the material models used in the FE model generated by ParaSpine
        /// </summary>
        [DataMember]
        public SpineMaterials Materials { get; private set; }

        /// <summary>
        /// Anatomical parameters for each vertebra and its subsequent (caudal/inferior) disc
        /// </summary>        
        [DataMember]
        public SpineAnatomy Anatomy { get; private set; }

        /// <summary>
        /// General FE model options (mesh resolution, target solver, meshed segment)
        /// </summary>
        [DataMember]
        public FEModelOptions Options { get; private set; }
        
        /// <summary>
        /// Create "empty" spine model (i.e. no vertebrae defined, default "null" materials)
        /// </summary>
        public SpineModel() {
            this.Anatomy = new SpineAnatomy();
            this.Materials = new SpineMaterials();
            this.Options = new FEModelOptions();
        }

        /// <summary>
        /// Loads a model parameterization from an XML file
        /// </summary>
        /// <param name="filename">Name of the file to be loaded</param>
        /// <returns>A new SpineModel</returns>
        /// <exception cref="System.ArgumentNullException">filename is null</exception>
        /// <exception cref="System.Security.SecurityException"></exception>
        /// <exception cref="System.IO.FileNotFoundException">A file with the name filename does not exist</exception>
        /// <exception cref="System.UriFormatException"></exception>
        public static SpineModel LoadFromXml(string filename) {
            SpineModel model;
            using (var reader = XmlDictionaryReader
                                .CreateDictionaryReader(XmlReader
                                                        .Create(filename))) {                                
                model = xmlSerializer.ReadObject(reader) as SpineModel;
            }
            return model;   
        }

        /// <summary>
        /// Saves the current SpineModel instance to an XML file
        /// </summary>
        /// <param name="filename">Name of the file to save to</param>
        public void SaveToXml(string filename) {
            using (var writer = XmlDictionaryWriter
                                .CreateDictionaryWriter(XmlWriter
                                                        .Create(filename, xmlWriterSettings))) {
                xmlSerializer.WriteObject(writer, this);
            }            
        }        
    }
}